﻿"use strict"
// QQ机器人
const { createClient } = require("icqq");
// rcon连接器
const _Rcon = require('modern-rcon');
// fs模块
const Fs = require("fs");
// 编码
const iconv = require("iconv-lite");
// 编码不显示警告
iconv.skipDecodeWarning = true;
// 引入订阅发布模式方法
const ev = require("./event");
export const event = new ev.eventJs();

/**
 * @param {Object} Bot 机器人配置
 * @param {Object} Rcon Rcon配置
 * @param {String} logFile 读取日志文件地址
 * 
 * 1. bot-message-watch 当前群聊消息监听
 * 2. bot-slider 滑块验证码
 * 3. bot-close 关闭机器人
 * 4. bot-online 登陆成功
 */
export class botClient {
    client = undefined; // 机器人实例
    Bot = undefined; // qq对象
    encode = false; // 是否启用编码

    Rcon = undefined; // rcon对象
    connStatus = false; // rcon连接状态
    stopStatus = false; // 是否通过stop指令断开服务器

    logFile = undefined; // 日志地址
    logStatus = false; // 日志读取状态

    stop = undefined; // 彻底断开机器人的方法暴露
    captcha = undefined; // 机器人滑动验证码暴露
    qrcode = undefined; // 机器人扫码登录方法暴露

    timeout = undefined;
    interval = undefined;
    heartbeat = undefined;

    constructor(Bot = {}, Rcon = {}, logFile = "", encode = true) {
        this.getters = require("@/store/index").default.getters;
        this.Bot = Bot;
        this.Rcon = Rcon;
        this.logFile = logFile;
        this.encode = encode;
        this.client = createClient({ platform: Bot.platform, log_level: 'off' });
        this.connStatus = false;
        // 方法暴露
        this.stop = this.close.bind(this);
        this.captcha = this.sendSlider.bind(this);
        this.qrcode = this.sendQrCode.bind(this);
    }
    // 机器人登录
    start() {
        this.triggerMessage("QQ登陆中...");
        this.client.on("system.login.error", (error) => {
            this.triggerMessage(`QQ登录失败：${error.message}`);
            this.triggerClose();
        }).on("system.login.slider", (ev) => {
            this.triggerCaptcha(1, ev.url);
        }).on('system.login.qrcode', (e) => {
            const _buffer = Buffer.from(e.image, "base64");
            const base64 = _buffer.toString("base64");
            this.triggerCaptcha(3, `data:image/png;base64,${base64}`);
        }).on("system.login.device", (ev) => {
            this.triggerCaptcha(2, ev.url);
            this.triggerMessage("扫码验证通过后，请重新登陆机器人！");
            this.triggerClose();
        }).on("system.offline.kickoff", () => {
            this.triggerMessage("该账号已在其他地方登录，请重新登陆！");
            this.triggerClose();
        }).on("system.offline.network", () => {
            this.triggerMessage("网络错误，机器人已自动下线！");
            this.triggerClose();
        }).on("system.online", () => {
            this.triggerOnline();
            this.triggerMessage("QQ登陆成功！");
            this.connStart();
        }).on("message.group", data => {
            // 监听群聊信息
            this.watchGroupMsg(data);
        });
        this.client.login(this.Bot.account, this.Bot.password);
    }
    // 提交滑块验证码
    sendSlider(code) {
        this.client.sliderLogin(code);
    }
    // 扫码登录
    sendQrCode(value) {
        // 登陆
        if (value === 'login') {
            this.triggerMessage("扫码完成，正在登录中...");
            this.client.qrcodeLogin();
        }
        // 刷新
        if (value === "refresh") {
            this.client.qrcodeLogin();
        }
    }
    // 向群内发送信息
    sendGroupMsg(msg) {
        this.client.sendGroupMsg(this.getters.groupId, msg).catch(err => {
            this.triggerMessage(`发送失败: ${err.message}（错误码: ${err.code}）`);
        });
    }
    // 心跳连接
    heartbeatStart() {
        this.heartbeat && clearInterval(this.heartbeat);
        // 如果不是本地服务器，开启心跳
        const localhost = ["0.0.0.0", "localhost", "127.0.0.1"]
        if (!localhost.includes(this.getters.RconHost)) {
            // 心跳连接，每5分钟发送一次心跳
            this.heartbeat = setInterval(() => {
                this.conn.send("list").then(() => {
                    this.triggerMessage("触发心跳连接！")
                }).catch(err => {
                    this.triggerMessage(`心跳连接失败：${err}`);
                });
            }, 1000 * 60 * 3);
        }
    }
    // rcon连接
    async connStart() {
        this.triggerMessage("连接服务器中...");
        if (this.conn && this.connStatus) {
            this.conn.disconnect();
            this.conn = undefined;
        }
        this.conn = new _Rcon(this.getters.RconHost, Number(this.Rcon.port), this.Rcon.password, 3000);
        try {
            await this.conn.connect();
            this.connStatus = true;
            this.stopStatus = false;
            this.triggerMessage("连接服务器成功！");
            this.heartbeatStart();
            this.watchFile();
        } catch {
            this.connStatus = false;
            this.triggerMessage("连接服务器失败！请检查服务器是否开启！");
            this.triggerClose();
        }
    }

    // stop指令重连
    stopRestart() {
        this.triggerMessage("监听到服务器停止，连接断开，2秒后重新连接服务器...");
        this.connStatus = false;
        this.stopStatus = true;
        this.timeout = setTimeout(() => {
            this.connStart();
            clearTimeout(this.timeout);
        }, 1000 * 2);
    }

    // 指令发送-主要功能
    command(data) {
        const senderId = data.senderId;
        const nickName = data.nickName;
        const message = data.message;
        // 截取指令
        let text = message.substring(this.getters.trigger.length, message.length);
        // 主命令
        let command = text.split(" ")[0];
        // 语句
        let msg = text.replace(`${command} `, "");
        // 实际要发送的
        let sendMsg = text;
        // 当命令是白名单的 || 发送人是管理员
        const white = this.getters.whiteCommandList.find(ev => command === ev);
        const master = this.getters.master.includes(senderId);
        if (white || master) {
            // say指令发送到服务器的格式化
            if (command === "say") {
                sendMsg = `say <${nickName}> ${msg}`
            }
            if (this.connStatus) {
                this.conn.send(sendMsg).then(str => {
                    if (!str.toLowerCase().includes("rcon")) {
                        this.sendGroupMsg(str.replace(/§[A-Za-z0-9]/g, ""));
                    }
                    // stop指令监听重新连接rcon
                    if (command === 'stop') this.stopRestart()
                    // 重新计时心跳
                    this.heartbeatStart();
                }).catch((err) => {
                    this.triggerMessage("发生错误：" + err);
                    this.sendGroupMsg("连接超时或服务器正在重启，正在重新连接");
                    this.connStart();
                });
            } else {
                this.sendGroupMsg("连接超时或服务器正在重启，正在重新连接");
                !this.stopStatus && this.connStart();
            }
        } else {
            let whiteCommStr = "";
            this.getters.whiteCommandList.map(str => {
                whiteCommStr += "\n" + str;
            })
            this.sendGroupMsg(`非管理员，无权限使用~\n当前开放命令：${whiteCommStr}`);
        }
    }

    /**
     * 监听-开始
     */

    // 监听群聊信息
    watchGroupMsg(data) {
        // 监听当前的群聊
        if (data.group_id == this.getters.groupId) {
            // 原始信息
            let message = data.message.find(ev => ev.type === "text").text;
            /**
             * @param {String} senderId 消息发送人id
             * @param {String} nickName 消息发送人昵称
             * @param {String} message 消息内容
             */
            const params = {
                senderId: data.user_id,
                nickName: data.sender.card || data.sender.nickname,
                message: message
            }
            this.triggerMessage(`${params.nickName}<${params.senderId}>：${params.message}`);
            // 正则判断是否是指令
            const reg = new RegExp(`^\\${this.getters.trigger}*$`, "i");
            // 指令条件判断
            if (message[0] === this.getters.trigger && !reg.test(message)) this.command(params);
        }
    }
    // 监听日志文件
    watchFile() {
        // 未检测到日志路径
        if (!this.logFile) return;
        this.triggerMessage("开启服务器日志监听！");
        if (this.logStatus) return;
        let filePath = this.logFile.replace(/\\/g, "/");
        let config = {
            persistent: true,
            interval: 150,
        };
        Fs.watchFile(filePath, config, (cur, prv) => {
            if (cur.mtime !== prv.mtime) {
                let config2 = {
                    encoding: "binary",
                };
                Fs.readFile(filePath, this.encode ? config2 : null, (err, data) => {
                    if (err) {
                        this.logStatus = false;
                        this.triggerMessage(`日志监听发生错误：${err}`);
                    } else {
                        this.logStatus = true;
                        // 以换行分割每一条日志
                        const dataList = data.toString().split("\n");
                        // 最后一条记录是换行导致为空
                        let lastMsg = dataList[dataList.length - 2];
                        // 正则替换掉日志中的颜色
                        lastMsg = lastMsg && lastMsg.replace(/\x1b\x5b([\x30-\x39]*\x3b)*[\x30-\x39]*\x6d/g, "")
                        // 服务器人为停止，rcon重新连接
                        if (lastMsg.toLowerCase().includes("stopping the server")) this.stopRestart();
                        // 数据格式校验并格式化
                        const reg = /]: <([^<>]*)>/g;
                        const match = reg.exec(lastMsg);
                        if (match && match[1].toLowerCase() !== "rcon") {
                            // 根据索引位切割，加上3是因为有"]: "长度为3
                            const splitIndex = match.index + 3;
                            const msg = lastMsg.substring(splitIndex, lastMsg.length);
                            const curMessage = iconv.decode(msg, "gbk");
                            // 发送信息
                            this.sendGroupMsg(this.encode ? curMessage : msg);
                        }
                    }
                });
            }
        });
    }

    /**
     * 监听-结束
     */

    /**
     * 订阅信息-开始
     */

    // 订阅信息-消息监听
    triggerMessage(msg) {
        if (this.getters.isOnline) {
            event.trigger("bot-message-watch", msg);
        }
    }
    // 订阅信息-验证码
    triggerCaptcha(type, url) {
        if (this.getters.isOnline) {
            event.trigger("bot-captcha", { type, url });
        }
    }
    // 订阅信息-自动关闭
    triggerClose() {
        event.trigger("bot-close");
    }
    // 订阅信息-登陆成功
    triggerOnline() {
        event.trigger("bot-online");
    }

    /**
     * 订阅信息-结束
     */

    // 关闭服务
    close() {
        this.timeout && clearTimeout(this.timeout)
        this.interval && clearInterval(this.interval)
        this.heartbeat && clearInterval(this.heartbeat)
        // 退出登录
        this.client.logout();
        // 断开连接服务器
        if (this.connStatus) {
            this.conn.disconnect();
            this.connStatus = false;
            this.conn = undefined;
        }
    }
}